﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Engine.Cameras;

namespace Engine.Graphics
{
    #region User Model

    public class UserModel<VertexType, IndexType> : DrawableEntity
        where VertexType : struct, IVertexType
        where IndexType : struct
    {
        GraphicsDevice device;
        VertexBuffer vb;
        IndexBuffer ib;

        public Effect Effect { get; set; }
        //BasicEffect defaultEffect;

        public int VertexCount { get; private set; }

        protected VertexType[] vertices;
        IndexType[] indices;

        #region Constructor

        public UserModel(Entity parent, int vertexCount)
            : base(parent)
        {
            this.device = Game.GraphicsDevice;
            VertexCount = vertexCount;

            vertices = new VertexType[vertexCount];

            Effect = new BasicEffect(device)
            {
                DiffuseColor = Color.White.ToVector3(),
                LightingEnabled = false,
                FogEnabled = false,
                PreferPerPixelLighting = false,
                TextureEnabled = true,
            };
        }

        #endregion

        #region Create Vertex / Index Buffers

        protected void CreateVertexBuffer()
        {
            vb = new VertexBuffer(device, typeof(VertexType), VertexCount, BufferUsage.None);
            vb.SetData(vertices);
        }

        protected void CreateIndexBuffer(IndexType[] indices)
        {
            this.indices = indices;
            ib = new IndexBuffer(device, typeof(IndexType), indices.Length, BufferUsage.None);
            ib.SetData(this.indices);
        }

        #endregion

        #region Draw

        public override void Draw(GameTime gameTime)
        {
            IEffectMatrices effectMatrices = Effect as IEffectMatrices;

            if (effectMatrices != null)
            {
                effectMatrices.World = localWorld;
                effectMatrices.View = Camera.ActiveCamera.View;
                effectMatrices.Projection = Camera.ActiveCamera.Projection;
            }

            Draw(Effect);
            base.Draw(gameTime);
        }

        public void Draw(Effect effect)
        {
            for (int i = 0; i < effect.CurrentTechnique.Passes.Count; i++)
            {
                EffectPass pass = effect.CurrentTechnique.Passes[i];
                pass.Apply();

                bool hasVertexBuffer = vb != null;
                bool hasIndexBuffer = ib != null;

                if (hasVertexBuffer && hasIndexBuffer)
                {
                    device.SetVertexBuffer(vb);
                    device.Indices = ib;
                    device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, ib.IndexCount / 3);
                }
                else if (!hasVertexBuffer && hasIndexBuffer)
                {

                    if (typeof(IndexType) == typeof(short))
                    {
                        device.DrawUserIndexedPrimitives<VertexType>(
                            PrimitiveType.TriangleList,
                            vertices,
                            0,
                            vertices.Length,
                            this.indices as short[],
                            0,
                            ib.IndexCount / 3);
                    }
                    else
                        device.DrawUserIndexedPrimitives<VertexType>(
                            PrimitiveType.TriangleList,
                            vertices,
                            0,
                            vertices.Length,
                            this.indices as int[],
                            0,
                            ib.IndexCount / 3);
                }
                else if (hasVertexBuffer && !hasIndexBuffer)
                {
                    device.SetVertexBuffer(vb);
                    device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, vertices.Length - 2);

                }
                else
                    device.DrawUserPrimitives<VertexType>(PrimitiveType.TriangleStrip, vertices, 0, vertices.Length - 2);

            }

        }

        #endregion

        #region IDisposable

        //public void Dispose()
        //{
        //    Dispose(true);
        //}

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                vb.Dispose();
                vb = null;

                ib.Dispose();
                ib = null;
            }
        }

        #endregion
    }

    #endregion
}
